#include <string.h> // for strlen
#include <iostream>
using namespace std;

#define DEBUG

// 剑指offer 面试题1：赋值运算符函数
// https://github.com/zhulintao/CodingInterviewChinese2/blob/master/01_AssignmentOperator/AssignmentOperator.cpp
class CMyString {
public:
    CMyString(const char* pData = nullptr);
    CMyString(const CMyString& str);
    ~CMyString(void);

    // 补充
    CMyString& operator=(const CMyString& rhs);
    // 重载输出流运算符，方便测试
    friend ostream& operator<<(ostream& os, const CMyString& rhs);

private:
    char* m_pData;
};

// 构造
CMyString::CMyString(const char* pData) {
    if (pData != nullptr) {
        m_pData = new char[strlen(pData) + 1]();
        strcpy(m_pData, pData);
    }
    else {
        // 无参构造 或者 传入空指针
        m_pData = new char[1]();
    }
}

// 拷贝构造
CMyString::CMyString(const CMyString& str) 
    : m_pData(new char[strlen(str.m_pData) + 1]())
{
    strcpy(m_pData, str.m_pData);
}

#ifdef DEBUG
// 赋值运算符函数
// 经典做法
// 异常安全性？ 回收了自己原来的空间之后，赋值的这个字符串太长了，要申请内存，此时内存不足，就会导致new char抛出异常
// 然后返回nullptr给m_pData，很容易导致程序崩溃
CMyString& CMyString::operator=(const CMyString& rhs) {
    if (this != &rhs) { // 1. 避免自复制
        // 2. 回收原来的空间
        delete [] m_pData;
        m_pData = nullptr;

        // 3. 分配新的空间
        m_pData = new char[strlen(rhs.m_pData) + 1]();

        // 4. 深拷贝
        strcpy(m_pData, rhs.m_pData);
    }
    return *this;
}
#endif

#ifndef DEBUG
// 赋值运算符函数
// 异常安全性做法
CMyString& CMyString::operator=(const CMyString& rhs) {
    if (this != &rhs) { // 1. 避免自复制
        // 2. 创建一个临时对象
        CMyString tmp(rhs); // 拷贝构造

        // 3. 暂存待复制内容的地址
        char* tmp_m_pData = tmp.m_pData;

        // 4. 把自己所指的字符串内容交给临时对象
        tmp.m_pData = m_pData;

        // 5. 自己指向临时对象的内容，完成复制
        m_pData = tmp_m_pData;

        // 6. 离开作用域会析构临时对象，从而释放原来的空间
    }
    return *this;
}
#endif

// 重载输出流运算符，方便测试
ostream& operator<<(ostream& os, const CMyString& rhs) {
    if (rhs.m_pData != nullptr) {
        // 防止输出nullptr导致段错误
        os << rhs.m_pData;
    } 
    return os;
}

// 析构
CMyString::~CMyString() {
    if (m_pData != nullptr) {
        delete [] m_pData;
        m_pData = nullptr;
    }
}

void test() {
    char* p = nullptr;
    // 段错误
    cout << strlen(p) << endl;
}

void test2() {
    // 测试经典做法的赋值运算符函数
    CMyString s;
    cout << s << endl;

    CMyString s1("hello world");
    cout << s1 << endl;

    s = s1;
    cout << s << endl;

    // 自赋值
    s = s;
    cout << s << endl;

    // 连续赋值
    CMyString s2("I love C++");
    s = s1 = s2;
    cout << s << endl;
    cout << s1 << endl;
    cout << s2 << endl;
}

int main(void)
{
    /* test(); */
    test2();
    return 0;
}

